package cn.miju.admin.system.service.impl;
import java.time.LocalDateTime;

import cn.hutool.core.collection.CollUtil;
import cn.miju.admin.system.service.MenuService;
import cn.miju.admin.system.vo.*;
import cn.miju.admin.system.vo.MenuTreeOutVO.Meta;
import cn.miju.database.entity.SysMenu;
import cn.miju.database.entity.SysMenuAuthRole;
import cn.miju.database.mapper.SysMenuAuthRoleMapper;
import cn.miju.database.mapper.SysMenuMapper;
import cn.miju.database.service.SysMenuAuthRoleDataService;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.common.collect.Lists;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * <pre>
 * 系统菜单
 * </pre>
 *
 * @author zhuming
 * @version 1.0.0
 * @since 2023-03-29 11:50
 */
@Service
@RequiredArgsConstructor
public class MenuServiceImpl implements MenuService {
    private final SysMenuAuthRoleMapper sysMenuAuthRoleMapper;
    private final SysMenuMapper sysMenuMapper;
    private final SysMenuAuthRoleDataService sysMenuAuthRoleDataService;

    /**
     * 获取系统菜单
     * @param roleId 角色ID
     * @return {@link MenuListOutVO}
     */
    @Override
    public MenuListOutVO getMenuList(Long roleId) {
        List<SysMenu> sysMenuList = sysMenuMapper.selectList(Wrappers.lambdaQuery());
        List<SysMenu> roleMenuList = sysMenuAuthRoleMapper.getMenuListByRoleIds(Lists.newArrayList(roleId));
        List<Long> roleMenuIdList = Lists.newArrayList();
        if (CollUtil.isNotEmpty(roleMenuList)) {
            roleMenuIdList = roleMenuList.stream().map(SysMenu::getId).collect(Collectors.toList());
        }
        if (CollUtil.isNotEmpty(sysMenuList)) {
            //一级菜单
            List<SysMenu> firstMenuList = sysMenuList.stream().filter(sysMenu -> null == sysMenu.getParentId()).collect(Collectors.toList());
            List<Long> firstMenuIdList = firstMenuList.stream().map(SysMenu::getId).collect(Collectors.toList());
            //二级菜单
            List<SysMenu> secondMenuList = sysMenuList.stream().filter(sysMenu -> firstMenuIdList.contains(sysMenu.getParentId())).collect(Collectors.toList());
            List<Long> secondMenuIdList = secondMenuList.stream().map(SysMenu::getId).collect(Collectors.toList());
            //三级菜单
            List<SysMenu> thirdMenuList = sysMenuList.stream().filter(sysMenu -> secondMenuIdList.contains(sysMenu.getParentId())).collect(Collectors.toList());
            //分组
            Map<Long, List<SysMenu>> firstToSecondMap = secondMenuList.stream().collect(Collectors.groupingBy(SysMenu::getParentId, Collectors.mapping(sysMenu -> sysMenu, Collectors.toList())));
            Map<Long, List<SysMenu>> secondToThirdMap = thirdMenuList.stream().collect(Collectors.groupingBy(SysMenu::getParentId, Collectors.mapping(sysMenu -> sysMenu, Collectors.toList())));
            MenuListOutVO menuListOutVO = new MenuListOutVO();
            menuListOutVO.setRoleMenuIds(roleMenuIdList);
            List<MenuListOutVO.MenuInfo> menuInfos = firstMenuList.stream().map(sysMenu -> {
                MenuListOutVO.MenuInfo firstMenuInfo = new MenuListOutVO.MenuInfo();
                firstMenuInfo.setId(sysMenu.getId());
                firstMenuInfo.setName(sysMenu.getName());
                firstMenuInfo.setPath(sysMenu.getPath());
                firstMenuInfo.setComponent(sysMenu.getComponent());
                firstMenuInfo.setMeta(MenuListOutVO.Meta.builder().title(sysMenu.getTitle()).type(sysMenu.getType()).icon(sysMenu.getIcon()).build());
                List<MenuListOutVO.MenuInfo> secondMenuInfoList = Lists.newArrayList();
                if (firstToSecondMap.containsKey(sysMenu.getId())) {
                    firstToSecondMap.get(sysMenu.getId()).forEach(secondMenu -> {
                        MenuListOutVO.MenuInfo secondMenuInfo = new MenuListOutVO.MenuInfo();
                        secondMenuInfo.setId(secondMenu.getId());
                        secondMenuInfo.setName(secondMenu.getName());
                        secondMenuInfo.setPath(secondMenu.getPath());
                        secondMenuInfo.setComponent(secondMenu.getComponent());
                        secondMenuInfo.setMeta(MenuListOutVO.Meta.builder().title(secondMenu.getTitle()).type(secondMenu.getType()).icon(secondMenu.getIcon()).build());
                        List<MenuListOutVO.MenuInfo> thirdMenuInfoList = Lists.newArrayList();
                        if (secondToThirdMap.containsKey(secondMenu.getId())) {
                            secondToThirdMap.get(secondMenu.getId()).forEach(thirdMenu -> {
                                MenuListOutVO.MenuInfo thirdMenuInfo = new MenuListOutVO.MenuInfo();
                                thirdMenuInfo.setId(thirdMenu.getId());
                                thirdMenuInfo.setName(thirdMenu.getName());
                                thirdMenuInfo.setPath(thirdMenu.getPath());
                                thirdMenuInfo.setComponent(thirdMenu.getComponent());
                                thirdMenuInfo.setMeta(MenuListOutVO.Meta.builder().title(thirdMenu.getTitle()).type(thirdMenu.getType()).icon(thirdMenu.getIcon()).build());
                                thirdMenuInfoList.add(thirdMenuInfo);
                            });
                        }
                        secondMenuInfo.setChildren(thirdMenuInfoList);
                        secondMenuInfoList.add(secondMenuInfo);
                    });
                }
                firstMenuInfo.setChildren(secondMenuInfoList);
                return firstMenuInfo;
            }).collect(Collectors.toList());
            menuListOutVO.setMenus(menuInfos);
            return menuListOutVO;
        }
        return MenuListOutVO.builder().menus(Collections.emptyList()).build();
    }
    /**
     * 获取用户菜单
     * @param roleIds 角色ID
     * @return {@link UserMenuListOutVO}
     */
    @Override
    public UserMenuListOutVO getUserMenuList(List<Long> roleIds) {
        List<SysMenu> sysMenuList = sysMenuAuthRoleMapper.getMenuListByRoleIds(roleIds);
        if (CollUtil.isNotEmpty(sysMenuList)) {
            //一级菜单
            List<SysMenu> firstMenuList = sysMenuList.stream().filter(sysMenu -> null == sysMenu.getParentId()).collect(Collectors.toList());
            List<Long> firstMenuIdList = firstMenuList.stream().map(SysMenu::getId).collect(Collectors.toList());
            //二级菜单
            List<SysMenu> secondMenuList = sysMenuList.stream().filter(sysMenu -> firstMenuIdList.contains(sysMenu.getParentId())).collect(Collectors.toList());
            List<Long> secondMenuIdList = secondMenuList.stream().map(SysMenu::getId).collect(Collectors.toList());
            //三级菜单
            List<SysMenu> thirdMenuList = sysMenuList.stream().filter(sysMenu -> secondMenuIdList.contains(sysMenu.getParentId())).collect(Collectors.toList());
            //分组
            Map<Long, List<SysMenu>> firstToSecondMap = secondMenuList.stream().collect(Collectors.groupingBy(SysMenu::getParentId, Collectors.mapping(sysMenu -> sysMenu, Collectors.toList())));
            Map<Long, List<SysMenu>> secondToThirdMap = thirdMenuList.stream().collect(Collectors.groupingBy(SysMenu::getParentId, Collectors.mapping(sysMenu -> sysMenu, Collectors.toList())));
            UserMenuListOutVO userMenuListOutVO = new UserMenuListOutVO();
            List<UserMenuListOutVO.MenuInfo> menuInfos = firstMenuList.stream().map(sysMenu -> {
                UserMenuListOutVO.MenuInfo firstMenuInfo = new UserMenuListOutVO.MenuInfo();
                firstMenuInfo.setName(sysMenu.getName());
                firstMenuInfo.setPath(sysMenu.getPath());
                firstMenuInfo.setComponent(sysMenu.getComponent());
                firstMenuInfo.setMeta(UserMenuListOutVO.Meta.builder().title(sysMenu.getTitle()).type(sysMenu.getType()).icon(sysMenu.getIcon()).build());
                List<UserMenuListOutVO.MenuInfo> secondMenuInfoList = Lists.newArrayList();
                if (firstToSecondMap.containsKey(sysMenu.getId())) {
                    firstToSecondMap.get(sysMenu.getId()).forEach(secondMenu -> {
                        UserMenuListOutVO.MenuInfo secondMenuInfo = new UserMenuListOutVO.MenuInfo();
                        secondMenuInfo.setName(secondMenu.getName());
                        secondMenuInfo.setPath(secondMenu.getPath());
                        secondMenuInfo.setComponent(secondMenu.getComponent());
                        secondMenuInfo.setMeta(UserMenuListOutVO.Meta.builder().title(secondMenu.getTitle()).type(secondMenu.getType()).icon(secondMenu.getIcon()).build());
                        List<UserMenuListOutVO.MenuInfo> thirdMenuInfoList = Lists.newArrayList();
                        if (secondToThirdMap.containsKey(secondMenu.getId())) {
                            secondToThirdMap.get(secondMenu.getId()).forEach(thirdMenu -> {
                                UserMenuListOutVO.MenuInfo thirdMenuInfo = new UserMenuListOutVO.MenuInfo();
                                thirdMenuInfo.setName(thirdMenu.getName());
                                thirdMenuInfo.setPath(thirdMenu.getPath());
                                thirdMenuInfo.setComponent(thirdMenu.getComponent());
                                thirdMenuInfo.setMeta(UserMenuListOutVO.Meta.builder().title(thirdMenu.getTitle()).type(thirdMenu.getType()).icon(thirdMenu.getIcon()).build());
                                thirdMenuInfoList.add(thirdMenuInfo);
                            });
                        }
                        secondMenuInfo.setChildren(thirdMenuInfoList);
                        secondMenuInfoList.add(secondMenuInfo);
                    });
                }
                firstMenuInfo.setChildren(secondMenuInfoList);
                return firstMenuInfo;
            }).collect(Collectors.toList());
            userMenuListOutVO.setMenus(menuInfos);
            return userMenuListOutVO;
        }
        return UserMenuListOutVO.builder().menus(Collections.emptyList()).build();
    }

    /**
     * 获取菜单树
     * @return {@link List<MenuTreeOutVO>}
     */
    @Override
    public List<MenuTreeOutVO> getMenuTree() {
        List<SysMenu> sysMenuList = sysMenuMapper.selectList(Wrappers.lambdaQuery());
        if (CollUtil.isNotEmpty(sysMenuList)) {
            //一级菜单
            List<SysMenu> firstMenuList = sysMenuList.stream().filter(sysMenu -> null == sysMenu.getParentId()).collect(Collectors.toList());
            List<Long> firstMenuIdList = firstMenuList.stream().map(SysMenu::getId).collect(Collectors.toList());
            //二级菜单
            List<SysMenu> secondMenuList = sysMenuList.stream().filter(sysMenu -> firstMenuIdList.contains(sysMenu.getParentId())).collect(Collectors.toList());
            List<Long> secondMenuIdList = secondMenuList.stream().map(SysMenu::getId).collect(Collectors.toList());
            //三级菜单
            List<SysMenu> thirdMenuList = sysMenuList.stream().filter(sysMenu -> secondMenuIdList.contains(sysMenu.getParentId())).collect(Collectors.toList());
            //分组
            Map<Long, List<SysMenu>> firstToSecondMap = secondMenuList.stream().collect(Collectors.groupingBy(SysMenu::getParentId, Collectors.mapping(sysMenu -> sysMenu, Collectors.toList())));
            Map<Long, List<SysMenu>> secondToThirdMap = thirdMenuList.stream().collect(Collectors.groupingBy(SysMenu::getParentId, Collectors.mapping(sysMenu -> sysMenu, Collectors.toList())));
            return firstMenuList.stream().map(sysMenu -> {
                MenuTreeOutVO outVO = new MenuTreeOutVO();
                outVO.setId(sysMenu.getId());
                outVO.setParentId(sysMenu.getParentId());
                outVO.setName(sysMenu.getName());
                outVO.setPath(sysMenu.getPath());
                outVO.setComponent(sysMenu.getComponent());
                outVO.setMeta(Meta.builder().title(sysMenu.getTitle()).type(sysMenu.getType()).icon(sysMenu.getIcon()).build());
                if (firstToSecondMap.containsKey(sysMenu.getId())) {
                    outVO.setChildren(firstToSecondMap.get(sysMenu.getId()).stream().map(secondMenu -> {
                        MenuTreeOutVO secondOutVO = new MenuTreeOutVO();
                        secondOutVO.setId(secondMenu.getId());
                        secondOutVO.setParentId(secondMenu.getParentId());
                        secondOutVO.setName(secondMenu.getName());
                        secondOutVO.setPath(secondMenu.getPath());
                        secondOutVO.setComponent(secondMenu.getComponent());
                        secondOutVO.setMeta(MenuTreeOutVO.Meta.builder().title(secondMenu.getTitle()).type(secondMenu.getType()).icon(secondMenu.getIcon()).build());
                        if (secondToThirdMap.containsKey(secondMenu.getId())) {
                            secondOutVO.setChildren(secondToThirdMap.get(secondMenu.getId()).stream().map(thirdMenu -> {
                                MenuTreeOutVO thirdOutVO = new MenuTreeOutVO();
                                thirdOutVO.setId(thirdMenu.getId());
                                thirdOutVO.setParentId(thirdMenu.getParentId());
                                thirdOutVO.setName(thirdMenu.getName());
                                thirdOutVO.setPath(thirdMenu.getPath());
                                thirdOutVO.setComponent(thirdMenu.getComponent());
                                thirdOutVO.setMeta(MenuTreeOutVO.Meta.builder().title(thirdMenu.getTitle()).type(thirdMenu.getType()).icon(thirdMenu.getIcon()).build());
                                return thirdOutVO;
                            }).collect(Collectors.toList()));
                        }
                        return secondOutVO;
                    }).collect(Collectors.toList()));
                }
                return outVO;
            }).collect(Collectors.toList());
        }
        return Collections.emptyList();
    }

    /**
     * 保存菜单
     * @param inVO {@link MenuSaveInVO}
     * @return 菜单ID
     */
    @Override
    public Long saveMenu(MenuSaveInVO inVO) {
        SysMenu sysMenu = new SysMenu();
        sysMenu.setId(inVO.getId());
        sysMenu.setParentId(inVO.getParentId());
        sysMenu.setName(inVO.getName());
        sysMenu.setTitle(inVO.getMeta().getTitle());
        sysMenu.setType(inVO.getMeta().getType());
        sysMenu.setPath(inVO.getPath());
        sysMenu.setIcon(inVO.getMeta().getIcon());
        sysMenu.setComponent(inVO.getComponent());
        sysMenu.setUpdatedTime(LocalDateTime.now());
        if (null == inVO.getId()) {
            sysMenuMapper.insert(sysMenu);
        } else {
            sysMenuMapper.updateById(sysMenu);
        }
        return sysMenu.getId();
    }
    /**
     * 授权菜单
     * @param inVO {@link AuthMenuInVO}
     */
    @Override
    public void authMenu(AuthMenuInVO inVO) {
        final long roleId = inVO.getRoleId();
        List<SysMenu> roleMenuList = sysMenuAuthRoleMapper.getMenuListByRoleIds(Lists.newArrayList(roleId));
        List<Long> addAuthMenuIdList = inVO.getMenuIds();
        if (CollUtil.isNotEmpty(roleMenuList)) {
            List<Long> roleMenuIdList = roleMenuList.stream().map(SysMenu::getId).collect(Collectors.toList());
            addAuthMenuIdList = inVO.getMenuIds().stream().filter(menuId -> !roleMenuIdList.contains(menuId)).collect(Collectors.toList());
            List<Long> deleteAuthMenuIdList = roleMenuIdList.stream().filter(menuId -> !inVO.getMenuIds().contains(menuId)).collect(Collectors.toList());
            if (CollUtil.isNotEmpty(deleteAuthMenuIdList)) {
                sysMenuAuthRoleMapper.delete(Wrappers.<SysMenuAuthRole>lambdaQuery().
                        eq(SysMenuAuthRole::getRoleId, roleId).
                        in(SysMenuAuthRole::getMenuId, deleteAuthMenuIdList));
            }
        }
        List<SysMenuAuthRole> sysMenuAuthRoleList = addAuthMenuIdList.stream().map(menuId -> {
            SysMenuAuthRole sysMenuAuthRole = new SysMenuAuthRole();
            sysMenuAuthRole.setMenuId(menuId);
            sysMenuAuthRole.setRoleId(roleId);
            return sysMenuAuthRole;
        }).collect(Collectors.toList());
        sysMenuAuthRoleDataService.saveBatch(sysMenuAuthRoleList);

    }

    /**
     * 批量删除菜单
     * @param inVO {@link MenuDeleteInVO}
     */
    @Override
    public void deletes(MenuDeleteInVO inVO) {
        sysMenuMapper.deleteBatchIds(inVO.getIds());
        sysMenuAuthRoleMapper.delete(Wrappers.<SysMenuAuthRole>lambdaQuery().in(SysMenuAuthRole::getMenuId, inVO.getIds()));
    }
}
